/*
 *  benchmark.c
 *  benchmark-perf
 *
 *  Created by Charles on 10-6-21.
 *  Copyright 2010 __MyCompanyName__. All rights reserved.
 *
 */

#include "benchmark.h"

void dobench(void* param)
{
	int i;
	void(* func)(void* param);
	struct bench * p = (struct bench *) param;
	struct timeval tvStart,tvEnd;
	func = p->func;
	
	syslog(LOG_NOTICE, "Thread[%lld] dobench variables inited.\n",pthread_self());
	
	//起始时间
	gettimeofday(&tvStart,NULL);
	
	if (p->n > 0) {
		for (i=0; i < p->n; i++) {
			func((void*)p->param);
		}
	}
	else {
		syslog(LOG_NOTICE, "struct bench * p->n should graet than 0\n");
	}
	
	//终止时间
	gettimeofday(&tvEnd,NULL);
	
	double dif;
	dif = 1000000*(tvEnd.tv_sec-tvStart.tv_sec) + (tvEnd.tv_usec-tvStart.tv_usec);
	
	syslog(LOG_NOTICE,"Thread[%lld] Cost => %f\n",pthread_self(),dif);
	
	/*
	 加锁->计数器加一->条件变量触发->解锁
	 每当一个线程迭代完成指定的任务时,都要通过条件变量通知主线程.
	 */
	pthread_mutex_lock(&p->mutex);
	p->count++;
	pthread_cond_signal(&p->cond);
	pthread_mutex_unlock(&p->mutex);
	
	return;
}

void call_threads(struct bench * p)
{
	struct timeval tvStart,tvEnd;
	double dif;
	long i;
	
	//起始时间
	gettimeofday(&tvStart,NULL);
	
	//初始化互斥锁及条件变量
	pthread_cond_init(&p->cond,NULL);
	pthread_mutex_init(&p->mutex,NULL);
	
	pthread_t tid;
	for(i = 0; i < p->num; ++i)
	{
		/* 通过多线程并发执行 */
		if (pthread_create(&tid,NULL,dobench,p) < 0) {
			syslog(LOG_ERR, "Create thread failed. - %m\n");
		}
		syslog(LOG_NOTICE, "Created a thread.\n");
	}
	
	//加锁->等待条件变量触发->解锁
	pthread_mutex_lock(&p->mutex);
	while(p->count < p->num)
	{
		pthread_cond_wait(&p->cond,&p->mutex);
		
		//终止时间
		gettimeofday(&tvEnd,NULL);
		
		dif = 1000000*(tvEnd.tv_sec-tvStart.tv_sec) + (tvEnd.tv_usec-tvStart.tv_usec);
		
		if (dif > 0)
		{
			syslog(LOG_NOTICE,"TotalThreads %d AlreadyFinishedThreads %d Iterations %d Timespentd %lf(microseconds) CurrentTPS => %lf\n",p->num,p->count,p->n,(p->count)*(p->n)*1000000.0/dif);
		}
		else{
			syslog(LOG_ERR,"Time dif[%f] < 0 \n",dif);
		}
	}
	pthread_mutex_unlock(&p->mutex);
}

void dofork(struct bench * p,int n)
{
	if (fork()==0) {
		syslog(LOG_NOTICE, "Child process forked. pid: [%ld], ppid: [%ld]",getpid(),getppid());
		p->num = n;
		call_threads(p);
		exit(errno);
	}
}

void dispatch(struct bench * p)
{
	pid_t pid;
	int group = p->num/BM_GROUP_THREADS;
	int	mod = p->num%BM_GROUP_THREADS;
	int i;
	
	for (i = 0; i < group; i++) {
		dofork(p,BM_GROUP_THREADS);
	}
	if (mod != 0)
		dofork(p,mod);
}

void waitall()
{
	int status;
	do {
		status = wait(NULL);
		if(status == -1 && errno != ECHILD) {
			syslog(LOG_ERR, "wait child process error! - %m");
			//abort();
		}
	} while (status > 0);
}

void setopenfiles(void *p)
{
	//设置可用文件数
	struct rlimit rt;
	if (((struct bench *)p)->num > 0)
		rt.rlim_max = rt.rlim_cur = ((struct bench *)p)->num*1024;
    if (setrlimit(RLIMIT_NOFILE, &rt) == -1) 
    {
        syslog(LOG_ERR, "setrlimit error. - %m");
        exit(1);
    }
}

void benchmark(void* param)
{
	struct bench * p = (struct bench *) param;
	struct timeval tvStart,tvEnd;
	double dif;
	
	//setopenfiles(p);
	
	//日志初始化
	openlog(logname, LOG_PID|LOG_CONS, LOG_USER);
	setlogmask(LOG_UPTO(loglevel));
	
	//起始时间
	gettimeofday(&tvStart,NULL);
	
	dispatch(p);
	
	//等待所有进程终止
	waitall();
	
	//终止时间
	gettimeofday(&tvEnd,NULL);
	
	dif = 1000000*(tvEnd.tv_sec-tvStart.tv_sec) + (tvEnd.tv_usec-tvStart.tv_usec);
	
	syslog(LOG_NOTICE,"Total Cost => %lf(microseconds)\n",dif);
	if (dif > 0)
	{
		syslog(LOG_NOTICE,"TPS => %lf\n",(p->num)*(p->n)*1000000.0/dif);
	}
	else{
		syslog(LOG_ERR,"Time dif[%f] < 0 \n",dif);
	}
	
	//关闭日志
	closelog();
}

void commandline(void* param)
{
	char buf[256];
	FILE *fp;
	if(!(fp=popen((const char *)param,"r")))
		syslog(LOG_ERR, "popen {%s} failed. - %m",(const char *)param);
	if (bm_opts.v != 0){
		while (fread(buf, 1, sizeof(buf), fp)) {
			fputs(buf, stdout);
		}
	}
	if (!pclose(fp))
		syslog(LOG_ERR, "close popen failed. - %m");
}

void concurrence(void* param)
{
	//这里完成单线程多次执行的代码
	
	//例子
	char uuidstr[36];
	uuid_t buf;
	uuid_generate_random(buf);
	uuid_unparse(buf,uuidstr);
	printf("concurrence func get uuid => %s\n",uuidstr);
}

int main (int argc, char const *argv[])
{
	char ch;
	
	//参数初始化,设置默认参数
	loglevel = LOG_DEBUG;
	strcpy(logname,"Benchmark");
	bp.n = 100;
	bp.num = 10;
	bp.param = (char *)malloc(8);
	memset(bp.param,0,8);
	
	//处理argv
	while( ( ch = getopt( argc, argv, "n:c:l:g:o:tv?" ) ) != EOF )
	{
		switch(ch)
		{
			case 'n':
				printf("Each thread iterations times =>%s\n", optarg);
				bp.n = atoi(optarg);
				break;
			case 'c':
				printf("Concurrence NUM => %s\n", optarg);
				bp.num = atol(optarg);
				break;
			case 'l':
				loglevel = atol(optarg);
				printf("Syslog level => %d.\n",loglevel);
				break;
			case 'g':
				strcpy(logname,optarg);
				printf("Syslog name => %s.\n",logname);
				break;
			case 't':
				bm_opts.t = 1;
				break;
			case 'v':
				bm_opts.v = 1;
				break;
			case 'o':
				if (strlen(optarg) > 0) {
					bp.param = malloc(strlen(optarg));
					strcpy(bp.param,optarg);
					((char *)bp.param)[strlen(optarg)] = '\0';
					printf("Command line => %s.\n",(char*)bp.param);
					break;
				}else {
					printf("error about format of command line => %s.first char => %c, last char => %c\n",optarg,optarg[0],optarg[sizeof(optarg)-1]);
					exit(1);
				}
			case '?':
				printf("-o [\"Command line\"] -n [Each thread iterations times] -c [Concurrence NUM] -l [Syslog level] -g [Syslog name,less than 128 char] -v [show -o stdoutput] -t [selftest].\n");
				exit(2);
			default:
				printf("Not support option :%c\n",ch);
		}
	}
	
	/*
	 例子:
	 */
	if (bm_opts.t != 0) {
		bp.func = concurrence;
		bp.param = NULL;
	}else if(strlen(bp.param) > 0){
		bp.func = commandline;
	}else {
		fprintf(stderr,"Error:Need a command.");
	}

	benchmark((void *)&bp);
	
	return 0;
}
